#include <queue>
#include "BSException/BSException.h"
#include "Log/SystemLog.h"
#include "MessageQueue.h"

using namespace SystemTool::Buffer;

// Construct the shared queue with a fixed size and name
MessageQueue::MessageQueue() :
	m_pMsgQueue(NULL),
	m_strQueueName("BASE_TOOL_MQ"),
	m_iQueueSize(64*1024),
	m_iElementSize(sizeof(int)),
	m_Disposed(false)
{
	Initialize();
}

// Detructe the MessageQueue
MessageQueue::~MessageQueue()
{
	Destory();
}

// Initialize the shared queue
MessageQueue::None MessageQueue::Initialize()
{

}

// Dispose the shared queue
MessageQueue::None MessageQueue::Destory()
{
	if (!GetDisposed())
	{
		SetDisposed(true);

		Close();
	}
}

// Configure the queue
MessageQueue::None MessageQueue::Configure(const std::string strQueueName, 
	const int32_t iQueueSize,
	const int32_t iElementSize)
{
	if (!strQueueName.empty())
	{
		SetQueueName(strQueueName);
	}
	
	if (iQueueSize > 1)
	{
		SetQueueSize(iQueueSize);
	}

	if (iElementSize > 0)
	{
		SetElementSize(iElementSize);
	}
}

// Is the shared queue opened or not
MessageQueue::Boolean MessageQueue::IsOpen()
{
	if (GetManagedQueue())
	{
		return true;
	}

	return false;
}

// Destory the share queue
MessageQueue::Boolean MessageQueue::DestoryQueue()
{
	if (!bip::message_queue::remove(GetQueueName().c_str()))
	{
		std::string strMsg = std::string("Failed to remove the queue:") + GetQueueName();

		LOG_ERROR_EX(strMsg);

		return false;
	}
	
	std::string strMsg = std::string("Successfully to remove the queue:") + GetQueueName();

	LOG_ERROR_EX(strMsg);

	return true;
}

// Open the map file
MessageQueue::Boolean MessageQueue::Open(MQOpenMode eOpenMode)
{
	try
	{
		if (eOpenMode == MQ_CREATE_ONLY)
		{
			// Destory the share queue
			DestoryQueue();

			SetManagedQueue(new bip::message_queue(bip::create_only,
				GetQueueName().c_str(),
				GetQueueSize(),
				GetElementSize()));
		}
		else if (eOpenMode == MQ_OPEN_ONLY)
		{
			SetManagedQueue(new bip::message_queue(bip::open_only, GetQueueName().c_str()));
		}
	}
	catch (boost::interprocess::interprocess_exception & e)
	{
		SetManagedQueue(NULL);

		int iErrorCode = e.get_error_code();

		std::string strMsg = std::string("Failed to create or open share queue,error code:")
			+ std::to_string(iErrorCode)
			+ " error msg:"
			+ e.what();

		LOG_ERROR_EX(strMsg);

		return false;
	}

	return true;
}

// Close the map file
MessageQueue::Boolean MessageQueue::Close()
{
	if (GetManagedQueue())
	{
		delete GetManagedQueue();

		SetManagedQueue(NULL);
	}

	return true;
}

// Blocking read the common queue.
MessageQueue::Boolean MessageQueue::Read(void* pData)
{
	bip::message_queue* pQueue = GetManagedQueue();

	if (pQueue == NULL)
	{
		LOG_ERROR_EX("Share queue is not opened yeap");

		return false;
	}

	try
	{
		unsigned int priority = 0;
		
		bip::message_queue::size_type recvd_size = 0;

		pQueue->receive(pData, GetElementSize(), recvd_size, priority);
	}
	catch (boost::interprocess::interprocess_exception & e)
	{
		int iErrorCode = e.get_error_code();

		std::string strMsg = std::string("Failed to read data from share queue,error code:")
			+ std::to_string(iErrorCode)
			+ " error msg:"
			+ e.what();

		LOG_ERROR_EX(strMsg);

		return false;
	}

	return true;
}

// Blocking write the queue
MessageQueue::Boolean MessageQueue::Write(void* pData)
{
	bip::message_queue* pQueue = GetManagedQueue();

	if (pQueue == NULL)
	{
		LOG_ERROR_EX("Share queue is not opened yeap");

		return false;
	}

	try
	{
		pQueue->send(pData, GetElementSize(), 0);
	}
	catch (boost::interprocess::interprocess_exception & e)
	{
		int iErrorCode = e.get_error_code();

		std::string strMsg = std::string("Failed to write data to share queue,error code:")
			+ std::to_string(iErrorCode)
			+ " error msg:"
			+ e.what();

		LOG_ERROR_EX(strMsg);

		return false;
	}

	return true;
}
